package com.terracloud.server.service.stats;

import com.terracloud.server.config.MetricsProperties;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Gauge;
import io.prometheus.client.exporter.PushGateway;
import lombok.extern.slf4j.Slf4j;
import oshi.SystemInfo;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import oshi.software.os.OSFileStore;
import oshi.hardware.CentralProcessor;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 物理资源监控指标服务
 * 负责收集系统物理资源使用情况并上报到PushGateway
 * 支持扩展多种类型的物理资源监控指标
 */
@Slf4j
public class PhysicalMetricsService {

    private final MetricsProperties metricsProperties;
    private final PushGateway pushGateway;
    private final CollectorRegistry registry;
    
    // 内存指标
    private final Gauge memoryTotalBytes;
    private final Gauge memoryAvailableBytes;
    
    // CPU指标
    private final Gauge cpuLoadPercentage;
    
    // 磁盘指标
    private final Gauge diskTotalBytes;
    private final Gauge diskAvailableBytes;
    
    // 网络指标
    private final Gauge networkBytesSent;
    private final Gauge networkBytesRecv;
    
    // 存储节点名称和租户ID的标签
    private final Map<String, String> labels = new HashMap<>();
    
    // 运行时启用状态
    private final AtomicBoolean runtimeEnabled = new AtomicBoolean(true);
    
    // 系统信息对象，用于收集指标
    private final SystemInfo systemInfo;

    public PhysicalMetricsService(MetricsProperties metricsProperties) {
        this.metricsProperties = metricsProperties;
        // 处理PushGateway URL，移除可能的协议前缀
        String pushGatewayUrl = metricsProperties.getUrl();
        if (pushGatewayUrl.startsWith("http://")) {
            pushGatewayUrl = pushGatewayUrl.substring(7);
        } else if (pushGatewayUrl.startsWith("https://")) {
            pushGatewayUrl = pushGatewayUrl.substring(8);
        }
        this.pushGateway = new PushGateway(pushGatewayUrl);
        this.registry = new CollectorRegistry();
        this.systemInfo = new SystemInfo();
        
        // 初始化标签
        labels.put("tenant_id", "terra-cloud-infra");
        labels.put("node_name", "平台服务器A");
        
        // 创建内存指标
        this.memoryTotalBytes = Gauge.build()
                .name("node_memory_total_bytes")
                .help("Total memory bytes")
                .labelNames("tenant_id", "node_name")
                .register(registry);
                
        this.memoryAvailableBytes = Gauge.build()
                .name("node_memory_available_bytes")
                .help("Available memory bytes")
                .labelNames("tenant_id", "node_name")
                .register(registry);
                
        // 创建CPU指标
        this.cpuLoadPercentage = Gauge.build()
                .name("node_cpu_load_percentage")
                .help("CPU load percentage")
                .labelNames("tenant_id", "node_name")
                .register(registry);
                
        // 创建磁盘指标
        this.diskTotalBytes = Gauge.build()
                .name("node_disk_total_bytes")
                .help("Total disk bytes")
                .labelNames("tenant_id", "node_name")
                .register(registry);
                
        this.diskAvailableBytes = Gauge.build()
                .name("node_disk_available_bytes")
                .help("Available disk bytes")
                .labelNames("tenant_id", "node_name")
                .register(registry);
                
        // 创建网络指标
        this.networkBytesSent = Gauge.build()
                .name("node_network_bytes_sent")
                .help("Network bytes sent")
                .labelNames("tenant_id", "node_name")
                .register(registry);
                
        this.networkBytesRecv = Gauge.build()
                .name("node_network_bytes_received")
                .help("Network bytes received")
                .labelNames("tenant_id", "node_name")
                .register(registry);
                
        // 初始化运行时启用状态
        this.runtimeEnabled.set(metricsProperties.isEnabled());
    }

    /**
     * 定时收集并上报物理资源指标
     * 默认每30秒执行一次
     */
    public void collectAndPushMetrics() {
        if (!isEnabled()) {
            log.debug("物理资源指标收集功能未启用");
            return;
        }
        
        try {
            collectPhysicalMetrics();
            pushMetrics();
            log.info("物理资源指标收集并上报成功");
        } catch (Exception e) {
            log.error("物理资源指标收集或上报失败", e);
        }
    }

    /**
     * 收集物理资源指标
     */
    private void collectPhysicalMetrics() {
        HardwareAbstractionLayer hal = systemInfo.getHardware();
        
        // 收集各类指标
        collectWithLogging("内存", () -> collectMemoryMetrics(hal));
        collectWithLogging("CPU", () -> collectCpuMetrics(systemInfo));
        collectWithLogging("磁盘", () -> collectDiskMetrics(systemInfo));
        collectWithLogging("网络", () -> collectNetworkMetrics(hal));
        
        log.debug("完成物理资源指标收集");
    }
    
    /**
     * 仅收集并推送CPU指标
     */
    public void collectAndPushCpuMetricOnly() {
        if (!isEnabled()) {
            log.debug("物理资源指标收集功能未启用");
            return;
        }
        
        try {
            // 仅收集CPU指标
            collectWithLogging("CPU", () -> collectCpuMetrics(systemInfo));
            pushMetrics();
            log.info("CPU指标收集并上报成功");
        } catch (Exception e) {
            log.error("CPU指标收集或上报失败", e);
            throw new RuntimeException("CPU指标收集或上报失败", e);
        }
    }
    
    /**
     * 仅收集并推送内存指标
     */
    public void collectAndPushMemoryMetricOnly() {
        if (!isEnabled()) {
            log.debug("物理资源指标收集功能未启用");
            return;
        }
        
        try {
            // 仅收集内存指标
            HardwareAbstractionLayer hal = systemInfo.getHardware();
            collectWithLogging("内存", () -> collectMemoryMetrics(hal));
            pushMetrics();
            log.info("内存指标收集并上报成功");
        } catch (Exception e) {
            log.error("内存指标收集或上报失败", e);
            throw new RuntimeException("内存指标收集或上报失败", e);
        }
    }
    
    /**
     * 仅收集并推送磁盘指标
     */
    public void collectAndPushDiskMetricOnly() {
        if (!isEnabled()) {
            log.debug("物理资源指标收集功能未启用");
            return;
        }
        
        try {
            // 仅收集磁盘指标
            collectWithLogging("磁盘", () -> collectDiskMetrics(systemInfo));
            pushMetrics();
            log.info("磁盘指标收集并上报成功");
        } catch (Exception e) {
            log.error("磁盘指标收集或上报失败", e);
            throw new RuntimeException("磁盘指标收集或上报失败", e);
        }
    }
    
    /**
     * 仅收集并推送网络指标
     */
    public void collectAndPushNetworkMetricOnly() {
        if (!isEnabled()) {
            log.debug("物理资源指标收集功能未启用");
            return;
        }
        
        try {
            // 仅收集网络指标
            HardwareAbstractionLayer hal = systemInfo.getHardware();
            collectWithLogging("网络", () -> collectNetworkMetrics(hal));
            pushMetrics();
            log.info("网络指标收集并上报成功");
        } catch (Exception e) {
            log.error("网络指标收集或上报失败", e);
            throw new RuntimeException("网络指标收集或上报失败", e);
        }
    }
    
    /**
     * 带日志记录的安全收集方法
     * 
     * @param metricType 指标类型
     * @param collector  指标收集器
     */
    private void collectWithLogging(String metricType, Runnable collector) {
        try {
            collector.run();
            log.debug("成功收集{}指标", metricType);
        } catch (Exception e) {
            log.warn("收集{}指标时发生错误: {}", metricType, e.getMessage(), e);
        }
    }

    /**
     * 收集内存指标
     * 
     * @param hal 硬件抽象层
     */
    private void collectMemoryMetrics(HardwareAbstractionLayer hal) {
        GlobalMemory memory = hal.getMemory();
        
        // 设置内存总量
        memoryTotalBytes.labels(getLabelValues())
                .set(memory.getTotal());
        
        // 设置可用内存量
        memoryAvailableBytes.labels(getLabelValues())
                .set(memory.getAvailable());
        
        log.trace("收集到物理内存指标: 总量={} bytes, 可用={} bytes", 
                memory.getTotal(), memory.getAvailable());
    }

    /**
     * 收集CPU指标
     * 
     * @param si 系统信息
     */
    private void collectCpuMetrics(SystemInfo si) {
        CentralProcessor processor = si.getHardware().getProcessor();
        
        // 获取CPU负载百分比
        double cpuLoad = processor.getSystemCpuLoadBetweenTicks(processor.getSystemCpuLoadTicks()) * 100;
        
        // 设置CPU负载
        cpuLoadPercentage.labels(getLabelValues())
                .set(cpuLoad);
        
        log.trace("收集到CPU指标: 负载={}%", cpuLoad);
    }

    /**
     * 收集磁盘指标
     * 
     * @param si 系统信息
     */
    private void collectDiskMetrics(SystemInfo si) {
        // 使用OSFileStore获取更准确的文件系统信息
        OSFileStore[] fileStores = si.getOperatingSystem().getFileSystem().getFileStores().toArray(new OSFileStore[0]);
        
        long totalSpace = 0;
        long usableSpace = 0;
        
        // 遍历所有文件系统获取总空间和可用空间
        for (OSFileStore fileStore : fileStores) {
            totalSpace += fileStore.getTotalSpace();
            usableSpace += fileStore.getUsableSpace();
        }
        
        // 设置磁盘总量
        diskTotalBytes.labels(getLabelValues())
                .set(totalSpace);
        
        // 设置磁盘可用量
        diskAvailableBytes.labels(getLabelValues())
                .set(usableSpace);
        
        log.trace("收集到磁盘指标: 总量={} bytes, 可用={} bytes", totalSpace, usableSpace);
    }

    /**
     * 收集网络指标
     * 
     * @param hal 硬件抽象层
     */
    private void collectNetworkMetrics(HardwareAbstractionLayer hal) {
        NetworkIF[] networkIFs = hal.getNetworkIFs().toArray(new NetworkIF[0]);
        
        long bytesSent = 0;
        long bytesRecv = 0;
        
        // 遍历所有网络接口获取发送和接收字节数
        for (NetworkIF networkIF : networkIFs) {
            bytesSent += networkIF.getBytesSent();
            bytesRecv += networkIF.getBytesRecv();
        }
        
        // 设置网络发送字节数
        networkBytesSent.labels(getLabelValues())
                .set(bytesSent);
        
        // 设置网络接收字节数
        networkBytesRecv.labels(getLabelValues())
                .set(bytesRecv);
        
        log.trace("收集到网络指标: 发送={} bytes, 接收={} bytes", bytesSent, bytesRecv);
    }

    /**
     * 获取标签值数组
     * 
     * @return 标签值数组
     */
    private String[] getLabelValues() {
        return new String[]{labels.get("tenant_id"), labels.get("node_name")};
    }

    /**
     * 上报指标到PushGateway
     *
     * @throws IOException 上报过程中发生IO异常
     */
    private void pushMetrics() throws IOException {
        Map<String, String> groupingKey = new HashMap<>();
        groupingKey.put("instance", metricsProperties.getInstanceId());
        pushGateway.pushAdd(registry, metricsProperties.getJobName(), groupingKey);
        log.info("成功将指标推送到PushGateway: http://{}/metrics/job/{}/instance/{}",
                metricsProperties.getUrl(), metricsProperties.getJobName(), metricsProperties.getInstanceId());
    }

    /**
     * 更新标签信息
     *
     * @param tenantId 租户ID
     * @param nodeName 节点名称
     */
    public void updateLabels(String tenantId, String nodeName) {
        labels.put("tenant_id", tenantId);
        labels.put("node_name", nodeName);
    }
    
    /**
     * 获取当前指标收集状态
     * 
     * @return true表示启用，false表示禁用
     */
    public boolean isEnabled() {
        return metricsProperties.isEnabled() && runtimeEnabled.get();
    }
    
    /**
     * 设置运行时启用状态
     * 
     * @param enabled 是否启用
     */
    public void setRuntimeEnabled(boolean enabled) {
        runtimeEnabled.set(enabled);
        log.info("设置运行时指标收集状态: {}", enabled);
    }
    
    /**
     * 获取运行时启用状态
     * 
     * @return 运行时启用状态
     */
    public boolean isRuntimeEnabled() {
        return runtimeEnabled.get();
    }
    
    /**
     * 获取系统信息对象（用于扩展）
     * 
     * @return SystemInfo对象
     */
    protected SystemInfo getSystemInfo() {
        return systemInfo;
    }
    
    /**
     * 获取标签映射（用于扩展）
     * 
     * @return 标签映射
     */
    protected Map<String, String> getLabels() {
        return new HashMap<>(labels);
    }
    
    /**
     * 获取指标配置属性
     * 
     * @return 指标配置属性
     */
    public MetricsProperties getMetricsProperties() {
        return metricsProperties;
    }
}